package com.one.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.one.common.core.constants.Constants;
import com.one.common.core.enums.ResultCode;
import com.one.common.security.exception.ServiceException;
import com.one.system.domain.exam.Exam;
import com.one.system.domain.exam.ExamQuestion;
import com.one.system.domain.exam.dto.ExamAddDTO;
import com.one.system.domain.exam.dto.ExamEditDTO;
import com.one.system.domain.exam.dto.ExamQueryDTO;
import com.one.system.domain.exam.dto.ExamQuestionAddDTO;
import com.one.system.domain.exam.vo.ExamQuestionVO;
import com.one.system.domain.exam.vo.ExamVO;
import com.one.system.domain.question.Question;
import com.one.system.domain.question.vo.QuestionVO;
import com.one.system.manager.ExamCacheManager;
import com.one.system.mapper.exam.ExamMapper;
import com.one.system.mapper.exam.ExamQuestionMapper;
import com.one.system.mapper.question.QuestionMapper;
import com.one.system.service.exam.IExamService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;
    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        //pagehelper帮助实现分页
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        return  examMapper.selectExamList(examQueryDTO);
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        checkExamSaveParams(examAddDTO,null);
        //添加不包含题目的竞赛基础信息
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO, exam);
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        //判断竞赛是否存在
        getExam(examQuestionAddDTO.getExamId());
        //判断所选择的题目是否存在
        Set<Long> setList = examQuestionAddDTO.getQuestionIdSet();
        checkQuestionId(setList);

        return saveExamQuestion(examQuestionAddDTO, setList);
    }

    @Override
    public ExamQuestionVO detail(Long examId) {
        //判断竞赛是否存在
        //首先通过id查询出竞赛的基本信息
        Exam exam = getExam(examId);
        ExamQuestionVO examQuestionVO = new ExamQuestionVO();
        BeanUtil.copyProperties(exam,examQuestionVO);
        //再通过竞赛id查询出相关的题目信息
        //select questionId from tb_exam_question where exam_id = ? order by
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion :: getQuestionId)
                .eq(ExamQuestion::getExamId, examId)
                .orderByDesc(ExamQuestion::getQuestionOrder));
        //不包含题目竞赛
        if(CollectionUtil.isEmpty(examQuestionList)){
            return examQuestionVO;
        }
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();

        List<Question> questionList = questionMapper.selectBatchIds(questionIdList);

        List<QuestionVO> questionVOList = BeanUtil.copyToList(questionList, QuestionVO.class);
        examQuestionVO.setExamQuestionList(questionVOList);
        return examQuestionVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        //判断，检查
        Exam exam = getExam(examEditDTO.getExamId());
        isExamStarted(exam);
        checkExamSaveParams(examEditDTO,examEditDTO.getExamId());
        //将基本信息更新到数据库
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int delete(Long examId) {
        //判断 竞赛是否存在，竞赛是否开赛
        Exam exam = getExam(examId);
        isExamStarted(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));

        return examMapper.deleteById(exam);
    }

    @Override
    public int publish(Long examId) {
        //判断前提条件，竞赛是否存在，竞赛中是否包含题目信息
        Exam exam = getExam(examId);
        if(exam.getEndTime().isBefore(LocalDateTime.now())){
            //结束时间在当前时间之前，不允许发布，暂时不操作历史竞赛
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();
        if(CollectionUtil.isEmpty(questionIdList)){
            throw  new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        //将未开始的竞赛信息存入redis当中，暂时不操作历史竞赛
        examCacheManager.addCache(exam);
        exam.setStatus(Constants.TRUE);
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        //判断前提条件
        //1.竞赛是否存在 2.竞赛是否开赛
        Exam exam = getExam(examId);
        if(exam.getEndTime().isBefore(LocalDateTime.now())){
            //结束时间在当前时间之前，不允许发布，暂时不操作历史竞赛
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        isExamStarted(exam);
        //将未开始的竞赛信息从redis中删除，暂时不操作历史竞赛
        examCacheManager.deleteCache(examId);
        exam.setStatus(Constants.FALSE);
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        // 需要判断，竞赛是否已经开始，开赛后不允许操作
        isExamStarted(exam);

        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion :: getExamId,examId)
                .eq(ExamQuestion :: getQuestionId,questionId));
    }

    private void isExamStarted(Exam exam) {
        if(exam.getStartTime().isBefore(LocalDateTime.now())){
            //竞赛已经开始
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    private void checkExamSaveParams(ExamAddDTO examSaveDTO,Long examId) {
        //判断是否有相同竞赛
        //判断更改后的竞赛是否已经存在 ,以及更改后的时间是否是符合条件的
        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select().eq(Exam :: getTitle, examSaveDTO.getTitle())
                .ne(examId != null,Exam::getExamId,examId));
        if(CollectionUtil.isNotEmpty(examList)){
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        //判断开始时间是否在当前时间之前
        if(examSaveDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if(!examSaveDTO.getStartTime().isAfter(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }

    private boolean saveExamQuestion(ExamQuestionAddDTO examQuestionAddDTO, Set<Long> setList) {
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        int num = 1;
        for (Long questionId : setList) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(examQuestionAddDTO.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);
        }
        return saveBatch(examQuestionList);
    }

    private void checkQuestionId(Set<Long> setList) {
        if(CollectionUtil.isEmpty(setList)){
            throw  new ServiceException(ResultCode.QUESTION_NOT_EXISTS);
        }
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if(exam == null){
            throw  new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }
}
